package spring.context;

import spring.BeansException;
import spring.annotation.*;
import spring.aop.AspectJAutoProxyRegistrar;
import spring.beans.BeanPostProcessor;
import spring.beans.config.BeanDefinition;
import spring.utils.ClassLoaderUtil;

import java.io.File;
import java.net.URL;

public class AnnotationConfigApplicationContext extends AbstractApplicationContext{


    public AnnotationConfigApplicationContext(Class configClass) {
        reader(configClass);
        refresh();
    }

    /**
     * 基于注解进行扫描注册
     * @param configClass
     */
    private void reader(Class configClass){

        // 解析@ComponentScan注册BeanDefinition
        processConfigBeanDefinitions(configClass);

        activeAspect(configClass);

    }

    /**
     * 自定义方法，激活aop
     * 如果存在该注解，我就把AspectJAutoProxyRegistrar加到容器中
     * @param configClass
     */
    private void activeAspect(Class configClass) {

        if(configClass.isAnnotationPresent(EnableAspectJAutoProxy.class)){
            System.out.println("单独注册：aspectJAutoProxyRegistrar");
            String beanName = "aspectJAutoProxyRegistrar";
            registry.registerBeanDefinition(beanName,new BeanDefinition(AspectJAutoProxyRegistrar.class,"singleton"));
        }
    }


    /**
     * 解析@ComponentScan注解，完成扫描，注册BeanDefinition
     * @param configClass
     */
    private void processConfigBeanDefinitions(Class configClass) {

        // 获得扫描路径
        ComponentScan componentScanAnnotation = (ComponentScan) configClass.getDeclaredAnnotation(ComponentScan.class);
        String path = componentScanAnnotation.value();

        /*
         * 获得目录资源
         */
        URL resource = ClassLoaderUtil.getClassLoader().getResource(path.replace(".", "/"));
        File dir = new File(resource.getFile());

        dfs(dir);
    }

    /**
     * 对目录下的class进行扫描过滤
     * 注册BeanDefinition
     */
    private void dfs(File file){
        if (file.isFile()){
            String filename = file.getAbsolutePath();
            if (filename.endsWith(".class")){
                // 转化类名格式以.分割
                String className = filename.substring(filename.indexOf("classes")+8, filename.indexOf(".class")).replace("\\", ".");
                try {
                    // 开始过滤
                    Class<?> clazz = ClassLoaderUtil.getClassLoader().loadClass(className);
                    if(clazz.isAnnotationPresent(Component.class)) {

                        String beanName = getBeanName(clazz);
                        BeanDefinition beanDefinition = new BeanDefinition();
                        // 设置scope
                        if (clazz.isAnnotationPresent(Scope.class)) {
                            Scope ScopeAnnotation = clazz.getDeclaredAnnotation(Scope.class);
                            beanDefinition.setScope(ScopeAnnotation.value());
                        } else {
                            beanDefinition.setScope("singleton");
                        }

                        beanDefinition.setClazz(clazz);
                        registry.registerBeanDefinition(beanName,beanDefinition);
                        System.out.println("注册BeanDefinition："+ beanName);
                    }
                } catch (ClassNotFoundException e) {
                    throw new RuntimeException(e);
                }
            }

        }

        if (file.isDirectory()){
            for (File f : file.listFiles()){
                dfs(f);
            }
        }
    }

    private String getBeanName(Class<?> clazz) {
        if(!clazz.isAnnotationPresent(Component.class)){
            throw new BeansException("no such bean");
        }
        Component component = (Component)clazz.getAnnotation(Component.class);
        if ("".equals(component.value())){
            // 获得类名
            String[] splits = clazz.getName().split("\\.");
            String tempBeanName = splits[splits.length-1];

            // 首字母小写
            char[] nameCharArray = tempBeanName.toCharArray();
            nameCharArray[0] += 32;

            return new String(nameCharArray);
        }
        return component.value();
    }
}
